﻿using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using Sustainalytics.BusinessResearchProductStates.Entities.ResearchState;
using Sustainalytics.Entities;
using Sustainalytics.Entities.RiskRatings;
using Sustainalytics.Entities.ScreeningTool;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace Sustainalytics.ScreeningTool.BL
{
    public static class CriteriaValueFormatter
    {
        private const string NoData = "No data";
        private const string NoInvolvement = "No involvement";
        private const string ResearchInProgress = "Research in progress";
        private const string FrameworkNotApplicable = "Framework not applicable";
        private const string NotResearched = "Not Researched";
        private const string NotResearchedCategoryOfInvolvement = "Category of Involvement";

        public static string FormatCriteriaValue(BsonDocument source, CriteriaResultOutput criteriaResultOutput)
        {
            var criteriaType = criteriaResultOutput.CriteriaType;
            var criteriaEntityField = criteriaResultOutput.CriteriaEntityField;
            var researchProducts = criteriaResultOutput.ResearchProducts;
            return FormatCriteriaValue(source, criteriaType, criteriaEntityField, researchProducts);
        }

        public static string FormatCriteriaValue(BsonDocument source, FilterCriteria filterCriteria)
        {
            var criteriaType = filterCriteria.CriteriaType;
            var criteriaEntityField = filterCriteria.CriteriaEntityField;
            var researchProducts = filterCriteria.ResearchProducts;
            return FormatCriteriaValue(source, criteriaType, criteriaEntityField, researchProducts);
        }

        public static string FormatCriteriaValue(BsonDocument source, CriteriaTypeEnum criteriaType, string criteriaEntityField, List<ResearchProduct> researchProducts)
        {
            var researchState = researchProducts.GetResearchState(source);

            var formattedValue = NoData;

            if (researchState == ResearchState.ResearchInProgress)
                formattedValue = ResearchInProgress;

            if (researchState == ResearchState.FrameworkNotApplicable)
                formattedValue = FrameworkNotApplicable;

            if (researchState == ResearchState.ResearchAvailable)
            {
                if (criteriaType == CriteriaTypeEnum.CriteriaType4 || criteriaType == CriteriaTypeEnum.CriteriaType9)
                    formattedValue = NoInvolvement;

                if (criteriaType == CriteriaTypeEnum.CriteriaType4)
                {
                    var subStrings = criteriaEntityField.Split(' ');
                    if (subStrings.Count() >= 2 && source.Contains($"{subStrings[0]} {subStrings[1]} {NotResearchedCategoryOfInvolvement}"))
                        formattedValue = NoData;
                }

                if (source.Contains(criteriaEntityField))
                    formattedValue = FormatCriteriaValue(source, criteriaType, criteriaEntityField);
            }

            return formattedValue;
        }

        private static string FormatCriteriaValue(BsonDocument source, CriteriaTypeEnum criteriaType, string criteriaEntityField)
        {
            switch (criteriaType)
            {
                case CriteriaTypeEnum.CriteriaType2:
                case CriteriaTypeEnum.CriteriaType8:
                    return FormatCriteriaType2Or8(source, criteriaEntityField);

                case CriteriaTypeEnum.CriteriaType4:
                    return FormatCriteriaType4(source, criteriaEntityField);

                case CriteriaTypeEnum.CriteriaType5:
                    return FormatCriteriaType5(source, criteriaEntityField);

                case CriteriaTypeEnum.CriteriaType25:
                    return FormatCriteriaType25(source, criteriaEntityField);

                case CriteriaTypeEnum.CriteriaType6:
                    return FormatCriteriaType6(source, criteriaEntityField);

                case CriteriaTypeEnum.CriteriaType7:
                    return FormatCriteriaType7(source, criteriaEntityField);

                case CriteriaTypeEnum.CriteriaType16:
                    return FormatCriteriaType16(source, criteriaEntityField);

                default:
                    return DefaultFormat(source, criteriaEntityField);
            }
        }

        private static string FormatCriteriaType2Or8(BsonDocument source, string criteriaEntityField)
        {
            if (criteriaEntityField.EndsWith("-Answer Category", StringComparison.InvariantCultureIgnoreCase))
            {
                if (source[criteriaEntityField] >= 1 && source[criteriaEntityField] <= 5)
                    return "Category " + source[criteriaEntityField];
                if (source[criteriaEntityField] == 0)
                    return "No evidence of relevant controversies";
            }

            if ((criteriaEntityField.EndsWith("-Weight", StringComparison.InvariantCultureIgnoreCase)
                || criteriaEntityField.EndsWith("-Weighted Score", StringComparison.InvariantCultureIgnoreCase))
                && source[criteriaEntityField].IsDouble)
                return source[criteriaEntityField].AsDouble.ToString("0.0000");

            return source[criteriaEntityField].ToString();
        }

        private static string FormatCriteriaType4(BsonDocument source, string criteriaEntityField)
        {
            if (criteriaEntityField.EndsWith("Revenue", StringComparison.Ordinal))
            {
                var value = source[criteriaEntityField].AsDouble;
                return value.ToString("0.00");
            }

            if (criteriaEntityField.EndsWith("Percentage", StringComparison.Ordinal) || criteriaEntityField.EndsWith("Revenue Percentage", StringComparison.Ordinal))
            {
                var value = source[criteriaEntityField].AsDouble;
                return value.ToString("0.00") + "%";
            }

            if (source[criteriaEntityField].AsString.ToLowerInvariant() == NotResearched.ToLowerInvariant())
                return NoData;

            return Regex.Replace(source[criteriaEntityField].AsString, @"<[^>]+>|&nbsp;", "").Trim();
        }

        private static string FormatCriteriaType5(BsonDocument source, string criteriaEntityField)
        {
            if (source[criteriaEntityField].IsDouble)
                return source[criteriaEntityField].AsDouble.ToString("0.0000");

            return source[criteriaEntityField].ToString();
        }

        private static string FormatCriteriaType25(BsonDocument source, string criteriaEntityField)
        {
            var returningValue = ((RiskRatingClusterAssessment)source[criteriaEntityField].AsInt32).ToString();
            return returningValue;
        }

        private static string FormatCriteriaType6(BsonDocument source, string criteriaEntityField)
        {
            if (source[criteriaEntityField].IsString)
            {
                return source[criteriaEntityField].AsString;
            }

            if (criteriaEntityField.EndsWith("Rank", StringComparison.Ordinal) || criteriaEntityField.EndsWith("Rank-Universe", StringComparison.Ordinal)
                || criteriaEntityField.EndsWith("Rank-Industry Group", StringComparison.Ordinal) || criteriaEntityField.EndsWith("Rank-Subindustry", StringComparison.Ordinal))
            {
                return BsonSerializer.Deserialize<PeerRanking>(source[criteriaEntityField] as BsonDocument).ToString();
            }

            if (criteriaEntityField.EndsWith("Percentile", StringComparison.Ordinal) || criteriaEntityField.EndsWith("Percentile-Universe", StringComparison.Ordinal)
                || criteriaEntityField.EndsWith("Percentile-Industry Group", StringComparison.Ordinal) || criteriaEntityField.EndsWith("Percentile-Subindustry", StringComparison.Ordinal))
            {
                var value = source[criteriaEntityField].AsDouble;
                return value.ToString("0.00") + "%";
            }

            if (criteriaEntityField.Contains("Momentum"))
            {
                return source[criteriaEntityField].AsDouble.ToString("0.00") + "%";
            }

            var fieldsWithNoDecimals = new[]
            {
                "Disclosure Score",
                "Preparedness Score",
                "Qualitative Performance Score",
                "Quantitative Performance Score"
            };

            if (fieldsWithNoDecimals.Contains(criteriaEntityField))
            {
                return source[criteriaEntityField].AsDouble.ToString(System.Globalization.CultureInfo.InvariantCulture);
            }

            return source[criteriaEntityField].AsDouble.ToString("0.00");
        }

        private static string FormatCriteriaType7(BsonDocument source, string criteriaEntityField)
        {
            if (criteriaEntityField != "Overall Global Compact Compliance Status")
                return source[criteriaEntityField].ToString();

            var OverallGlobalCompactComplianceStatusMap = new Dictionary<int, string>
            {
                [0] = "Compliant",
                [1] = "Watchlist",
                [2] = "Non-compliant",
                [9999] = NoData,
            };

            string value;
            var key = source[criteriaEntityField].AsInt32;
            OverallGlobalCompactComplianceStatusMap.TryGetValue(key, out value);
            return value;
        }

        private static string FormatCriteriaType16(BsonDocument source, string criteriaEntityField)
        {
            if (criteriaEntityField.StartsWith("CG - Rank - ", StringComparison.Ordinal))
            {
                return BsonSerializer.Deserialize<PeerRanking>(source[criteriaEntityField] as BsonDocument).ToString();
            }

            if (criteriaEntityField.StartsWith("CG - Percentile - ", StringComparison.Ordinal))
            {
                return source[criteriaEntityField].AsDouble.ToString("0.00") + "%";
            }

            return source[criteriaEntityField].AsDouble.ToString("0.00");
        }

        private static string DefaultFormat(BsonDocument source, string criteriaEntityField)
        {
            var bsonValue = source[criteriaEntityField];

            if (bsonValue.IsString)
                return Regex.Replace(bsonValue.AsString, @"<[^>]+>|&nbsp;", "").Trim();

            if (bsonValue.IsInt32)
                return bsonValue.AsInt32.ToString();

            if (bsonValue.IsDouble)
                return Math.Round(bsonValue.AsDouble, 2, MidpointRounding.AwayFromZero).ToString("0.00");

            return bsonValue.ToString();
        }
    }

    public class PeerRanking
    {
        public int Rank { get; set; }
        public int Peers { get; set; }

        public override string ToString() => $"{Rank} out of {Peers}";
    }
}
